package com.holystone.splunk.app.service.impl;

import com.github.pagehelper.PageInfo;
import com.holystone.orca.ms.MailUtil;
import com.holystone.splunk.app.enums.EventEnums;
import com.holystone.splunk.app.model.AlertVO;
import com.holystone.splunk.app.model.EventVO;
import com.holystone.splunk.app.mybatis.model.Alert;
import com.holystone.splunk.app.mybatis.model.Event;
import com.holystone.splunk.app.service.IIndexAlertService;
import com.holystone.splunk.app.service.IIndexEventService;
import com.holystone.splunk.common.SystemConstant;
import com.holystone.splunk.utils.*;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.Resource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.*;

/**
 * Copyright (c) 2017, HOLYSTONE Technologies, Inc.
 * All right reserved.
 *
 * @author huyan
 * @date 2017/12/4
 * @desc:
 */
@Service(value = "indexEventService")
public class IndexEventServiceImpl extends SplunkIndexServiceImpl implements IIndexEventService {

    private Logger logger = Logger.getLogger(this.getClass());
    private String webApp_path = System.getProperty("path.webapp");
    @Autowired
    private MailUtil mailUtil;
    @Resource(name = "indexAlertService")
    private IIndexAlertService indexAlertService;

    @Override
    public void saveEventAndAlerts(EventVO event, Set<AlertVO> modelList) {
        try {
            /**
             * 1：保存事件结果
             * 2：保存事件-告警的关联关系
             * 3：更改告警的状态为 已关联
             * 4：判断是否需要发送邮件
             */
            if (event != null) {
                event.setEventId(new Date().getTime());
                event.setDeleteFlag(0);
                event.setEventState(0);
                event.setCreateTime(new Date());
                Set<Alert> alerts = event.getAlerts();
                if (alerts == null) {
                    alerts = new HashSet<>();
                }
                if(modelList!=null){
                    for (AlertVO vo : modelList) {
                        alerts.add(AlertVO.getAlert(vo));
                    }
                }
                event.setAlerts(alerts);
            }
            //1.2.
            insertEvent2Index(event);

            //3.
            if(modelList!=null){
                for (AlertVO vo : modelList) {
                    Set<Event> events = vo.getEvents();
                    if (events == null) {
                        events = new HashSet<>();
                    }
                    Event se = EventVO.getEvent(event);
                    events.add(se);
                    vo.setAlertState(1);
                    vo.setEvents(events);
                    insertData2Index(SystemConstant.SPLUNK_INDEX, SystemConstant.SOURCETYPE_ALERT, JSONUtils.javaBean2Json(vo));
                }
            }
            //4.
            if (event.getIsSendEmail().intValue() == 1) {
                sendEmail(event, modelList);
            }
        } catch (Exception e) {
            logger.error("保存出错：" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void insertEvent2Index(EventVO event) throws IOException {
        logger.debug(">>>>> 保存事件 insertEvent2Index()");
        String eventJson = JSONUtils.javaBean2Json(event);
        insertData2Index(SystemConstant.SPLUNK_INDEX, SystemConstant.SOURCETYPE_EVENT, eventJson);
        logger.debug("<<<<< 保存事件 insertEvent2Index()");
    }

    @Override
    public List<EventVO> getEventByEventIds(String eventIds) throws IOException {
        logger.debug(">>>>> 获取事件列表 getEventByEventIds() eventIds:" + eventIds);
        if (eventIds == null || eventIds == "") {
            return null;
        }
        // 拼接查询
        String[] eventIdArr = eventIds.split(",");
        String query = "search index=" + SystemConstant.SPLUNK_INDEX +
                " sourcetype=" + SystemConstant.SOURCETYPE_EVENT +
                " |where deleteFlag = 0 and ";
        for (String eventId : eventIdArr) {
            query += " eventId=" + eventId + " or";
        }
        if (eventIdArr.length != 0) {
            query = query.substring(0, query.length() - 3);
        }
        query += " |transaction eventId";
        // 查询数据
        List<JsonNode> jsonList = getDataFromIndex(query, null, null, null, null);
        // 封装返回结果
        if (jsonList == null || jsonList.size() == 0) {
            return null;
        }
        List<EventVO> voList = new ArrayList<>();
        for (JsonNode jsonNode : jsonList) {
             /*
              使用 transaction函数，可能一条记录会返回多个对象，用"\n"分割，取最新的对象
             */
            String[] voArr = jsonNode.asText().split("\n");
            voList.add(JSONUtils.jsonStr2JavaBean(voArr[voArr.length - 1], EventVO.class));
        }
        logger.debug("<<<<< 获取事件列表 getEventByEventIds() eventIds:" + eventIds);
        return voList;
    }

    @Override
    public List<String> getExistsEventSorts() throws IOException {
        logger.debug(">>>>> 获取存在的事件类别 getExistsEventSorts()");
        List<String> names=null;
        try{
            // 初始化--必须
            EHCacheUtil.initCacheManager();
            //缓存30分钟
            EHCacheUtil.initCache(EHCacheConfig.PROJECR_YONGYUAN_CACHE,EHCacheConfig.PROJECR__CACHE_TIME_60);
            Object obj= EHCacheUtil.get(EHCacheConfig.PROJECR_YONGYUAN_CACHE_EVENT_KEY);
            if(obj==null){
                names = getDIYStatisticaData(SystemConstant.SPLUNK_INDEX, SystemConstant.SOURCETYPE_EVENT, "eventSort");
                EHCacheUtil.put(EHCacheConfig.PROJECR_YONGYUAN_CACHE_EVENT_KEY, names);
            }else{
                names =(List<String>)obj;
            }
        }catch (Exception e){
            logger.error(e,e);
        }
        logger.debug("<<<<< 获取存在的事件类别 getExistsEventSorts()");
        return names;
    }

    @Override
    public PageInfo<EventVO> getEventPageInfo(String pageNo, String pageSize, EventVO conditions) throws Exception {
        logger.debug(">>>>> 获取事件分页 getEventPageInfo()");
        Integer num = pageNo == null ? SystemConstant.DEFAULT_PAGE_NUM : Integer.parseInt(pageNo);
        Integer size = pageSize == null ? SystemConstant.DEFAULT_PAGE_SIZE : Integer.parseInt(pageSize);
        List<EventVO> voList = getEventsByConditions(num, size, conditions);
        Integer totalCount = getEventsTotalCountByConditions(conditions);
        Integer totalPages = totalCount%size == 0 ? totalCount/size : totalCount/size + 1;
        PageInfo<EventVO> info = new PageInfo<>();
        info.setList(voList);
        info.setPageNum(num);
        info.setPageSize(size);
        info.setTotal(totalCount);
        info.setPages(totalPages);
        logger.debug("<<<<< 获取事件分页 getEventPageInfo()");
        return info;
    }

    @Override
    public List<EventVO> getEventsByConditions(Integer pageNo, Integer pageSize, EventVO conditions) throws Exception {
        logger.debug(">>>>> 根据条件获取事件信息 getEventsByConditions()");
        // 封装query
        String query = "search index=" + SystemConstant.SPLUNK_INDEX +
                " sourcetype=" + SystemConstant.SOURCETYPE_EVENT +
                " |dedup eventId" +
                " |where deleteFlag = 0 ";
        if (conditions != null) {
            if (conditions.getEventSort() != null && conditions.getEventSort() != "") {
                query += " and like(eventSort, \"%" + conditions.getEventSort() + "%\")";
            }
            if (conditions.getEventType() != null && conditions.getEventType() != "") {
                query += " and like(eventType, \"%" + conditions.getEventType() + "%\")";
            }
            if (conditions.getEventOwner() != null && conditions.getEventOwner() != "") {
                query += " and like(eventOwner, \"%" + conditions.getEventOwner() + "%\")";
            }
            if (conditions.getEventState() != null) {
                query += " and eventState=" + conditions.getEventState();
            }
            if (conditions.getThreatLevel() != null) {
                query += " and threatLevel=" + conditions.getThreatLevel();
            }
        }
        // 查询数据
        Date earliest = null;
        Date latest = null;
        if (conditions.getEarliest() != null && conditions.getEarliest() != "") {
            earliest = DateFormatUtils.str2Date(conditions.getEarliest());
        }
        if (conditions.getLatest() != null && conditions.getLatest() != "") {
            latest = DateFormatUtils.str2Date(conditions.getLatest());
        }
        List<JsonNode> jsonList = getDataFromIndex(query, pageNo, pageSize, earliest, latest);
        // 封装返回数据
        if (jsonList == null) {
            return null;
        }
        List<EventVO> voList = new ArrayList<>();
        for (JsonNode jsonNode : jsonList) {
            voList.add(JSONUtils.jsonStr2JavaBean(jsonNode.asText(), EventVO.class));
        }
        logger.debug("<<<<< 根据条件获取事件信息 getEventsByConditions()");
        return voList;
    }

    @Override
    public Integer getEventsTotalCountByConditions(EventVO conditions) throws Exception {
        logger.debug(">>>>> 获取事件总个数 getEventsTotalCountByConditions()");
        // 封装查询
        String query = "search index=" + SystemConstant.SPLUNK_INDEX +
                " sourcetype=" + SystemConstant.SOURCETYPE_EVENT +
                " |dedup eventId" +
                " | where deleteFlag = 0";
        if (conditions != null) {
            if (conditions.getEventSort() != null && conditions.getEventSort() != "") {
                query += " and like(eventSort, \"%" + conditions.getEventSort() + "%\")";
            }
            if (conditions.getEventType() != null && conditions.getEventType() != "") {
                query += " and like(eventType, \"%" + conditions.getEventType() + "%\")";
            }
            if (conditions.getEventOwner() != null && conditions.getEventOwner() != "") {
                query += " and like(eventOwner, \"%" + conditions.getEventOwner() + "%\")";
            }
            if (conditions.getEventState() != null) {
                query += " and eventState=" + conditions.getEventState();
            }
            if (conditions.getThreatLevel() != null) {
                query += " and threatLevel=" + conditions.getThreatLevel();
            }
        }
        query += "|stats count as totalCount";
        // 查询数据
        Date earliest = null;
        Date latest = null;
        if (conditions.getEarliest() != null && conditions.getEarliest() != "") {
            earliest = DateFormatUtils.str2Date(conditions.getEarliest());
        }
        if (conditions.getLatest() != null && conditions.getLatest() != "") {
            latest = DateFormatUtils.str2Date(conditions.getLatest());
        }
        Integer totalCount = getDIYTotalCount(query, "totalCount", earliest, latest);
        logger.debug("<<<<< 获取事件总个数 getEventsTotalCountByConditions()");
        return totalCount;
    }

    @Override
    public String dealSameSource(String alertSid, String alertName, JsonNode result) {
        return "";
    }

    @Override
    public void updateEvent(EventVO event) throws IOException, MessagingException, ParseException {
        logger.debug(">>>>> 更新事件 updateEvent() eventId:" + event.getEventId());
        /**
         * 判断是否需要发送邮件
         */
        if (event.getIsSendEmail().intValue() == 1) {
            Set<AlertVO> voSet = new HashSet<>();
            voSet.addAll(AlertVO.getAlertVOs(event.getAlerts()));
            sendEmail(event, voSet);
        }
        insertEvent2Index(event);
        logger.debug(">>>>> 更新事件 updateEvent() eventId:" + event.getEventId());
    }


    @Override
    public PageInfo<AlertVO> getAlertsInfoByEventId(String pageNo, String pageSize, Long eventId) throws IOException {
        logger.debug(">>>>> 获取告警分页 getAlertsInfoByEventId() pageNo:" + pageNo + ", pageSize:" + pageSize);
        Integer num = pageNo == null ? SystemConstant.DEFAULT_PAGE_NUM : Integer.parseInt(pageNo);
        Integer size = pageSize == null ? SystemConstant.DEFAULT_PAGE_SIZE : Integer.parseInt(pageSize);
        EventVO event = getEventByEventIds(eventId + "").get(0);

        List<AlertVO> voList = new ArrayList<>();
        Integer totalCount = 0;
        Integer totalPages = 1;
        if (event.getAlerts() != null && event.getAlerts().size() != 0) {
            voList = indexAlertService.getAlertsByEvent(num, size, event.getAlerts());
            totalCount = indexAlertService.getAlertTotalCountByEvent(event.getAlerts());
            totalPages = totalCount%size == 0 ? totalCount/size : totalCount/size + 1;
        }
        PageInfo<AlertVO> info = new PageInfo<>();
        info.setList(voList);
        info.setPageNum(num);
        info.setPageSize(size);
        info.setTotal(totalCount);
        info.setPages(totalPages);
        logger.debug("<<<<< 获取告警分页 getAlertsInfoByEventId() pageNo:" + pageNo + ", pageSize:" + pageSize);
        return info;
    }

    @Override
    public void closeEvent(Long eventId, String email) throws IOException, MessagingException {
        logger.debug(">>>>> 关闭事件 closeEvent() eventId:" + eventId);
        /**
         * 事件关闭：
         * 1：将事件状态更新为关闭
         * 2：将事件关联的告警状态更新为关闭
         * 3：发送邮件
         */
        //1.
        EventVO event = getEventByEventIds(eventId + "").get(0);
        event.setEventState(1);
        insertEvent2Index(event);
        //2.
        Set<Alert> alerts = event.getAlerts();
        for (Alert alert : alerts) {
            AlertVO alertVO = indexAlertService.getAlertsBySids(alert.getAlertSid()).get(0);
            alertVO.setAlertState(2);
            indexAlertService.insertAlert2Index(alertVO);
        }
        //3.
        if (email != null && email != "") {
            List<String> emailList = new ArrayList<>();
            emailList.add(email);
            String title = "关闭事件";
            String content = "事件分类 " + event.getEventSort() +
                    " 事件分级 " + event.getEventType() +
                    " 事件创建时间 " + DateFormatUtils.date2Str(event.getCreateTime()) +
                    " 在 " + DateFormatUtils.date2Str(new Date()) +
                    " 关闭";
            mailUtil.send(emailList, title, content);
        }
        logger.debug("<<<<< 关闭事件 closeEvent() eventId:" + eventId);
    }

    private void sendEmail(EventVO event, Set<AlertVO> modelList) throws IOException, MessagingException, ParseException {
        String content = event.getEmailContent();
        content += "<div>" +
                "<table style='text-align:left;'>" +
                "<thead>" +
                "<tr>" +
                "<td style='padding:2px 10px;'>触发时间</td>" +
                "<td style='padding:2px 10px;'>告警名称</td>" +
                "<td style='padding:2px 10px;'>告警结果</td>" +
                "<td style='padding:2px 10px;'>事件分类</td>" +
                "<td style='padding:2px 10px;'>事件分级</td>" +
                "</tr>" +
                "</thead>" +
                "<tbody>";
        for (AlertVO vo : modelList) {
            String result = vo.getAlertResult();
            if (result != null && result != "") {
                JsonNode json = JSONUtils.jsonStr2JsonNode(result);
                if (json.isArray()) {
                    result = json.get(0).toString();
                } else {
                    result = "更多告警结果，请查看附件";
                }
            }else {
                result = "告警结果为原始日志，请查看附件";
            }
            content += "<tr>" +
                    "<td style='padding:2px 10px;'>" + DateFormatUtils.date2Str(vo.getTriggerTime()) + "</td>" +
                    "<td style='padding:2px 10px;'>" + vo.getAlertName() + "</td>" +
                    "<td style='padding:2px 10px;'>" + result + "</td>" +
                    "<td style='padding:2px 10px;'>" + event.getEventSort() + "</td>" +
                    "<td style='padding:2px 10px;'>" + EventEnums.EventLevelEnum.getLevelMsg(event.getThreatLevel()) + "</td>" +
                    "</tr>";
        }
        content += "</tbody></table>" +
                "</br></br></br> 详细日志/更多告警结果，请查看附件" +
                "</div>";

        // 标签和附件
        content+="<div><hr/></br>" +
                "Best regards! 诚挚问候！</br></br>" +
                "<img src='cid:1'></br></br>" +
                "<span>&nbsp;&nbsp;&nbsp;管理服务部安全运营中心</br></br>" +
                "&nbsp;&nbsp;&nbsp;管理服务部  Management and Services Department</br>" +
                "&nbsp;&nbsp;&nbsp;IT基础设施科 IT System Support Section</br>" +
                "&nbsp;&nbsp;&nbsp;电话 Tel: +86 431 85866414</br>" +
                "&nbsp;&nbsp;&nbsp;邮箱 E-mail: splunkad@faw-vw.com" +
                "</span><div>";//cid 代表图片填充位置
        // 邮件内容容器
        MimeMultipart mm = new MimeMultipart("mixed");
        // 添加邮件描述内容
        BodyPart mdp = new MimeBodyPart();// 新建一个存放信件内容的BodyPart对象
        mdp.setContent(content, "text/html;charset=utf-8");// 给BodyPart对象设置内容和格式/编码方
        mm.addBodyPart(mdp);
        //填充图片
        MimeBodyPart image = new MimeBodyPart();
        // 获取图片 封装
        String path = webApp_path + File.separator + "image" + File.separator + "yiqi.jpg";
        DataSource fd2 = new FileDataSource(path);
        image.setDataHandler(new DataHandler(fd2));
        image.setHeader("Content-ID", "<1>");
        mm.addBodyPart(image);

        //发送附件
        //假如有多个附件 List<File>
        List<File> attachments = readAttachFiles(modelList);
        // 添加附件
        if(attachments != null && attachments.size() != 0) {
            for(File attachment : attachments) {
                BodyPart attachmentPart = new MimeBodyPart();
                DataSource source = new FileDataSource(attachment);
                attachmentPart.setDataHandler(new DataHandler(source));
                //避免中文乱码的处理
                attachmentPart.setFileName(MimeUtility.encodeWord(attachment.getName()));
                mm.addBodyPart(attachmentPart);
            }
        }
        mailUtil.send(StringUtils.str2List(event.getEmailToAddress()), event.getEmailTitle(), mm);//mm是邮件容器  不再是内容  跟下面的不是一个方法
    }

    private List<File> readAttachFiles(Set<AlertVO> modelList) throws IOException, ParseException {
        List<File> list = new ArrayList<>();
        Map<String, List<JsonNode>> jsonMap = new HashMap<>();
        for (AlertVO alert : modelList) {
            String query = PropertiesUtils.readValue(alert.getAlertName());
            if (query != null && query != "") {
                query = query.replaceAll("alertSid", alert.getAlertSid());
                Date earliest = DateFormatUtils.str2Date(alert.getEarliestTime(), "yyyy/MM/dd:HH:mm:ss");
                Date latest = DateFormatUtils.str2Date(alert.getLatestTime(), "yyyy/MM/dd:HH:mm:ss");
                List<JsonNode> jsonList = getResultsFormSearch(query, earliest, latest);
                String sheetName = alert.getAlertName() + "_" + DateFormatUtils.date2Str(alert.getTriggerTime(), "yyyy-MM-dd-HH-mm-ss");
                jsonMap.put(sheetName, jsonList);
            }
        }
        File file = FileUtils.getExcelFile(jsonMap, webApp_path + File.separator + "image" + File.separator + "alert.xls");
        if (file != null) {
            list.add(file);
        }
        return list;
    }
}
